package com.anb.net;

import com.anb.common.Utils;
import com.google.protobuf.Parser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Dispatcher implements Packet.ICmdHelper {

    public Dispatcher(Class<?> cmdHandlerClass, Object[] cmdHandlerArgs) {
        this.cmdHandlerClass = cmdHandlerClass;
        this.cmdHandlerArgs = cmdHandlerArgs;
    }

    private Class<?> cmdHandlerClass;
    private Object[] cmdHandlerArgs;

    private List<CmdHandler> mHandlers = new ArrayList<CmdHandler>();
    private Map<Short, CmdHandler> mHandlersByCmd = new HashMap<Short, CmdHandler>();
    private Map<String, CmdHandler> mHandlersByName = new HashMap<String, CmdHandler>();

    private int sizeLimit;

    public CmdHandler getCmdHandler(short cmd) throws Exception {
        CmdHandler cmdHandler = mHandlersByCmd.get(cmd);
        if (cmdHandler == null) {
            String error = String.format("cannot get CmdHandler of 0X%04X", cmd);
            throw new Exception(error);
        }
        return cmdHandler;
    }

    public CmdHandler getCmdHandler(String cmd) throws Exception {
        CmdHandler cmdHandler = mHandlersByName.get(cmd);
        if (cmdHandler == null) {
            String error = String.format("cannot get CmdHandler of %s", cmd);
            throw new Exception(error);
        }
        return cmdHandler;
    }

    public void addHandler(CmdHandler cmdHandler) {
        mHandlers.add(cmdHandler);
        mHandlersByCmd.put(cmdHandler.cmd, cmdHandler);
        mHandlersByName.put(cmdHandler.name, cmdHandler);
    }

    public void clearHandlers() {
        mHandlers.clear();
        mHandlersByCmd.clear();
        mHandlersByName.clear();
    }

    public void load(File file, String[] namespaces) throws Exception {
        clearHandlers();

        Document document = Utils.loadDocument(file);
        sizeLimit = (int)Utils.parseBinary(document.getDocumentElement().getAttribute("size-limit"));

        NodeList nodeList = document.getElementsByTagName("cmd");

        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            if (node.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            Element element = (Element)node;
            CmdHandler cmdHandler = (CmdHandler)Utils.newInstance(cmdHandlerClass, cmdHandlerArgs);

            cmdHandler.cmd = Short.parseShort(element.getAttribute("id"), 16);
            cmdHandler.name = element.getAttribute("name");
            cmdHandler.in = Utils.getClass(element.getAttribute("in"), namespaces);
            cmdHandler.out = Utils.getClass(element.getAttribute("out"), namespaces);
            String sizeLimitStr = element.getAttribute("size-limit");
            cmdHandler.sizeLimit = (sizeLimitStr == null || sizeLimitStr.length() == 0) ?
                    sizeLimit : (int)Utils.parseBinary(sizeLimitStr);

            cmdHandler.inBody = Packet.BodyType.from(cmdHandler.in);
            cmdHandler.outBody = Packet.BodyType.from(cmdHandler.out);
            cmdHandler.description = String.format("%s|0X%04X", cmdHandler.name, cmdHandler.cmd);
            if (cmdHandler.inBody == Packet.BodyType.ProtoBuf) {
                cmdHandler.inParser = (Parser<?>)cmdHandler.in.getField("PARSER").get(null);
            }

            cmdHandler.prepare();
            addHandler(cmdHandler);
        }
    }
}
